package dryven.codegen.asm;

import org.objectweb.asm.Opcodes;

import dryven.codegen.asm.typeinfo.ClassTypeInfo;
import dryven.codegen.asm.typeinfo.TypeInfo;

public class LocalVariable extends ReadableAbstractInvokable implements Assignable, Indexable {

	private int index;
	private TypeInfo type;
	
	public LocalVariable(int index, MethodBuilder mb, TypeInfo type) {
		super(mb);
		this.index = index;
		this.type = type;
	}

	@Override
	public TempValue read() {
		if(type.isRepresentedByInt()) {
			getBuilder().getMethodVisitor().visitVarInsn(Opcodes.ILOAD, index);
		} else if(type.isFloat()) {
			getBuilder().getMethodVisitor().visitVarInsn(Opcodes.FLOAD, index);
		} else if(type.isLong()) {
			getBuilder().getMethodVisitor().visitVarInsn(Opcodes.LLOAD, index);
		} else if(type.isDouble()) {
			getBuilder().getMethodVisitor().visitVarInsn(Opcodes.DLOAD, index);
		} else {
			getBuilder().getMethodVisitor().visitVarInsn(Opcodes.ALOAD, index);
		}
		return new TempValue(type,getBuilder());
	}

	@Override
	public void assign(TempValue e) {
		e.checkValid();
		//TODO do more checks if types are compatible
		if(index==0) {
			throw new RuntimeException("Can't assign 'this'");
		}
		if(type.isRepresentedByInt()) {
			getBuilder().getMethodVisitor().visitVarInsn(Opcodes.ISTORE, index);
		} else if(type.isFloat()) {
			getBuilder().getMethodVisitor().visitVarInsn(Opcodes.FSTORE, index);
		} else if(type.isLong()) {
			getBuilder().getMethodVisitor().visitVarInsn(Opcodes.LSTORE, index);
		} else if(type.isDouble()) {
			getBuilder().getMethodVisitor().visitVarInsn(Opcodes.DSTORE, index);
		} else {
			getBuilder().getMethodVisitor().visitVarInsn(Opcodes.ASTORE, index);
		}
		e.pop();
	}

	@Override
	public TypeInfo getTypeInfo() {
		return type;
	}

	@Override
	public TempValue getArraySize() {
		TempValue me = read();
		getBuilder().getMethodVisitor().visitInsn(Opcodes.ARRAYLENGTH);
		me.pop();
		return new TempValue(new ClassTypeInfo(int.class),getBuilder());
	}

	@Override
	public ArrayEntry getArrayEntry(int index) {
		return new ArrayEntry(type,this,index,getBuilder());
	}
	

	@Override
	public FieldInstance getFieldInstance(FieldDefinition fd) {
		return new FieldInstance(fd, getBuilder(),this);
	}
}
